`timescale 1ns / 1ps

`include "data_width.vh"

module sequential_accumulator #(parameter
    VERTEX_PIPE_NUM = `VERTEX_PIPE_NUM,
    DST_ID_DWIDTH = `DST_ID_DWIDTH,
    VERTEX_BRAM_AWIDTH = `VERTEX_BRAM_AWIDTH, VERTEX_BRAM_DWIDTH = `VERTEX_BRAM_DWIDTH,
    WB_VALID_WIDTH = `WB_VALID_WIDTH
    ) (
        input                                                   clk,
        input                                                   front_rst,
        input [DST_ID_DWIDTH * VERTEX_PIPE_NUM - 1 : 0]         front_dst_id,
        input [VERTEX_BRAM_DWIDTH * VERTEX_PIPE_NUM - 1 : 0]    front_src,
        input [VERTEX_PIPE_NUM - 1 : 0]                         front_dst_data_valid,

        output                                                  rst,
        output [DST_ID_DWIDTH * VERTEX_PIPE_NUM - 1 : 0]        wb_dst_addr,
        output [VERTEX_BRAM_DWIDTH * VERTEX_PIPE_NUM - 1 : 0]   wb_dst_data,
        output [WB_VALID_WIDTH * VERTEX_PIPE_NUM - 1 : 0]       wb_dst_data_valid);
    
    sequential_accumulator_para_trans P (
        .clk(clk), .front_rst(front_rst),

        .rst(rst));

    generate
        genvar i;
        for (i = 0; i < VERTEX_PIPE_NUM; i = i + 1) begin : M16_BLOCK_1
            sequential_accumulator_pipeline P (
                .clk(clk), .rst(front_rst),
                .front_dst_id(front_dst_id[(i + 1) * DST_ID_DWIDTH - 1 : i * DST_ID_DWIDTH]),
                .front_src(front_src[(i + 1) * VERTEX_BRAM_DWIDTH - 1 : i * VERTEX_BRAM_DWIDTH]),
                .front_dst_data_valid(front_dst_data_valid[i]),

                .wb_dst_addr(wb_dst_addr[(i + 1) * DST_ID_DWIDTH - 1 : i * DST_ID_DWIDTH]),
                .wb_dst_data(wb_dst_data[(i + 1) * VERTEX_BRAM_DWIDTH - 1 : i * VERTEX_BRAM_DWIDTH]),
                .wb_dst_data_valid(wb_dst_data_valid[(i + 1) * WB_VALID_WIDTH - 1 : i * WB_VALID_WIDTH]));
        end
    endgenerate

endmodule

module sequential_accumulator_para_trans (
    input       clk,
    input       front_rst,

    output reg  rst);
    
    always @ (posedge clk) begin
        rst <= front_rst;
    end

endmodule

module sequential_accumulator_pipeline # (
    WB_VALID_WIDTH = `WB_VALID_WIDTH,
    VERTEX_BRAM_NUM_WIDTH = `VERTEX_BRAM_NUM_WIDTH, VERTEX_PIPE_NUM_WIDTH = `VERTEX_PIPE_NUM_WIDTH,
    VERTEX_BRAM_DWIDTH = `VERTEX_BRAM_DWIDTH,
    DST_ID_DWIDTH = `DST_ID_DWIDTH
    ) (
    input                                   clk,
    input                                   rst,
    input [DST_ID_DWIDTH - 1 : 0]           front_dst_id,
    input [VERTEX_BRAM_DWIDTH - 1 : 0]      front_src,
    input                                   front_dst_data_valid,

    output reg [DST_ID_DWIDTH - 1 : 0]      wb_dst_addr, // 使用 DST_ID_DWIDTH 便于调试
    output reg [VERTEX_BRAM_DWIDTH - 1 : 0] wb_dst_data,
    output reg [WB_VALID_WIDTH - 1 : 0]     wb_dst_data_valid);

    reg [DST_ID_DWIDTH - 1 : 0]         now_dst_addr;
    reg [VERTEX_BRAM_DWIDTH - 1 : 0]    now_dst_data;

    // 取出地址时进行映射
    always @ (posedge clk) begin
        if (rst == 1'b1) begin
            wb_dst_addr <= 0;
            wb_dst_data <= 0;
        end
        else begin
            if (front_dst_data_valid == 1'b1) begin
                if (now_dst_addr != 0 && now_dst_addr != front_dst_id) begin
                    wb_dst_addr <= now_dst_addr;
                    wb_dst_data <= now_dst_data;
                end
                else begin
                    wb_dst_addr <= 0;
                    wb_dst_data <= 0;
                end
            end
            else begin
                wb_dst_addr <= 0;
                wb_dst_data <= 0;
            end
        end
    end

    generate
        genvar i;
        for (i = 0; i < WB_VALID_WIDTH; i = i + 1) begin: M16_BLOCK_2
            always @ (posedge clk) begin
                if (rst) begin
                    wb_dst_data_valid[i] <= 1'b0;
                end
                else begin
                    if (front_dst_data_valid) begin
                        if (now_dst_addr != 0 && now_dst_addr != front_dst_id) begin
                            wb_dst_data_valid[i] <= (now_dst_addr[VERTEX_BRAM_NUM_WIDTH - 1 : VERTEX_PIPE_NUM_WIDTH] == i);
                        end
                        else begin
                            wb_dst_data_valid[i] <= 1'b0;
                        end
                    end
                    else begin
                        wb_dst_data_valid[i] <= 1'b0;
                    end
                end
            end
        end
    endgenerate

    always @ (posedge clk) begin
        if (rst == 1'b1) begin
            now_dst_addr <= 0;
            now_dst_data <= 0;
        end
        else begin
            if (front_dst_data_valid == 1'b1) begin
                if (now_dst_addr != front_dst_id || now_dst_data > front_src) begin
                    now_dst_addr <= front_dst_id;
                    now_dst_data <= front_src;
                end
            end
        end
    end

endmodule